package network

import (
	"Alpaca/log"
	"errors"
	"net"
	"sync"
	"time"
)

type TcpServer struct {
	//conf
	Addr         string
	MaxConn      int
	WriteChanLen int
	MinMsgLen    uint32
	MaxMsgLen    uint32

	//net
	listen     net.Listener
	conns      ConnSet
	connsMutex sync.Mutex
	listenWg   sync.WaitGroup
	connsWg    sync.WaitGroup
	NewAgent   func(*TcpConn) Agent

	msgParser *MsgParser
}

func (server *TcpServer) Start() {
	server.init()
	go server.run()
}

func (server *TcpServer) init() {
	listen, err := net.Listen("tcp", server.Addr) //新建监听
	if err != nil {
		log.Fatal("%v", err)
	}

	//判定非法值并赋默认值，确保服务器能正常运行
	if server.MaxConn <= 0 {
		server.MaxConn = 100
		log.Release("MaxConn is invalid,reset to %v", server.MaxConn)
	}
	if server.WriteChanLen <= 0 {
		server.WriteChanLen = 100
		log.Release("WriteChanLen is invalid,reset to %v", server.WriteChanLen)
	}
	if server.NewAgent == nil {
		log.Fatal("NewAgent could not be nil")
	}

	//成员对象创建
	server.listen = listen
	server.conns = make(ConnSet)
	server.msgParser = NewParser()
	server.msgParser.SetMsgLen(server.MinMsgLen, server.MaxMsgLen)
}

func (server *TcpServer) run() {
	server.listenWg.Add(1)
	defer server.listenWg.Done()

	var tempDelay time.Duration
	for {
		conn, err := server.listen.Accept()
		if err != nil {
			if errors.Is(err, net.ErrClosed) {
				log.Release("listener is closed")
				break
			}
			if tempDelay == 0 {
				tempDelay = time.Microsecond * 5
			} else {
				tempDelay *= 2
			}

			if tempDelay > time.Second {
				tempDelay = time.Second
			}
			log.Release("error %v,try again in %vs", err, tempDelay)
			time.Sleep(tempDelay)
			continue
		}
		tempDelay = 0

		//log.Debug("accept success")
		//添加连接
		server.connsMutex.Lock()
		if len(server.conns) >= server.MaxConn {
			//连接太多，无法继续添加
			conn.Close()
			server.connsMutex.Unlock()
			log.Debug("Too many connections")
			continue
		}
		server.conns[conn] = struct{}{}
		server.connsMutex.Unlock()

		//构建TcpConn
		tcpConn := NewTcpConn(conn, server.WriteChanLen, server.msgParser)
		agent := server.NewAgent(tcpConn) //创建agent代理conn
		server.connsWg.Add(1)
		go func() {
			agent.Run()

			//cleanup
			server.connsMutex.Lock()
			delete(server.conns, conn)
			server.connsMutex.Unlock()
			tcpConn.Close()

			server.connsWg.Done()
		}()
	}
}

func (server *TcpServer) Close() {
	server.listen.Close()
	server.listenWg.Wait()

	server.connsMutex.Lock()
	for conn := range server.conns {
		conn.Close()
	}
	server.conns = nil
	server.connsMutex.Unlock()
	server.connsWg.Wait()
}
